//===--- PILGenDecl.cpp - Implements Lowering of ASTs -> PIL for Decls ----===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#include "polarphp/pil/gen/Initialization.h"
#include "polarphp/pil/gen/LValue.h"
#include "polarphp/pil/gen/RValue.h"
#include "polarphp/pil/gen/PILGen.h"
#include "polarphp/pil/gen/PILGenDynamicCast.h"
#include "polarphp/pil/gen/Scope.h"
#include "polarphp/pil/gen/SwitchEnumBuilder.h"
#include "polarphp/ast/GenericEnvironment.h"
#include "polarphp/ast/NameLookup.h"
#include "polarphp/ast/InterfaceConformance.h"
#include "polarphp/basic/ProfileCounter.h"
#include "polarphp/pil/lang/PrettyStackTrace.h"
#include "polarphp/pil/lang/PILArgument.h"
#include "polarphp/pil/lang/PILDebuggerClient.h"
#include "polarphp/pil/lang/PILType.h"
#include "polarphp/pil/lang/TypeLowering.h"
#include "llvm/ADT/SmallString.h"
#include <iterator>

using namespace polar;
using namespace lowering;

void Initialization::_anchor() {}
void PILDebuggerClient::anchor() {}

namespace {
/// A "null" initialization that indicates that any value being initialized
/// into this initialization should be discarded. This represents AnyPatterns
/// (that is, 'var (_)') that bind to values without storing them.
class BlackHoleInitialization : public Initialization {
public:
   BlackHoleInitialization() {}

   bool canSplitIntoTupleElements() const override {
      return true;
   }

   MutableArrayRef<InitializationPtr>
   splitIntoTupleElements(PILGenFunction &SGF, PILLocation loc,
                          CanType type,
                          SmallVectorImpl<InitializationPtr> &buf) override {
      // "Destructure" an ignored binding into multiple ignored bindings.
      for (auto fieldType : cast<TupleType>(type)->getElementTypes()) {
         (void) fieldType;
         buf.push_back(InitializationPtr(new BlackHoleInitialization()));
      }
      return buf;
   }

   void copyOrInitValueInto(PILGenFunction &SGF, PILLocation loc,
                            ManagedValue value, bool isInit) override {
      /// This just ignores the provided value.
   }

   void finishUninitialized(PILGenFunction &SGF) override {
      // do nothing
   }
};
} // end anonymous namespace

static void copyOrInitValueIntoHelper(
   PILGenFunction &SGF, PILLocation loc, ManagedValue value, bool isInit,
   ArrayRef<InitializationPtr> subInitializations,
   llvm::function_ref<ManagedValue(ManagedValue, unsigned, PILType)> func) {
   auto sourceType = value.getType().castTo<TupleType>();
   auto sourcePILType = value.getType();
   for (unsigned i = 0, e = sourceType->getNumElements(); i != e; ++i) {
      PILType fieldTy = sourcePILType.getTupleElementType(i);
      ManagedValue elt = func(value, i, fieldTy);
      subInitializations[i]->copyOrInitValueInto(SGF, loc, elt, isInit);
      subInitializations[i]->finishInitialization(SGF);
   }
}

void TupleInitialization::copyOrInitValueInto(PILGenFunction &SGF,
                                              PILLocation loc,
                                              ManagedValue value, bool isInit) {
   // In the object case, emit a destructure operation and return.
   if (value.getType().isObject()) {
      return SGF.B.emitDestructureValueOperation(
         loc, value, [&](unsigned i, ManagedValue subValue) {
            auto &subInit = SubInitializations[i];
            subInit->copyOrInitValueInto(SGF, loc, subValue, isInit);
            subInit->finishInitialization(SGF);
         });
   }

   // In the address case, we forward the underlying value and store it
   // into memory and then create a +1 cleanup. since we assume here
   // that we have a +1 value since we are forwarding into memory.
   assert(value.isPlusOne(SGF) && "Can not store a +0 value into memory?!");
   value = ManagedValue::forUnmanaged(value.forward(SGF));
   return copyOrInitValueIntoHelper(
      SGF, loc, value, isInit, SubInitializations,
      [&](ManagedValue aggregate, unsigned i,
          PILType fieldType) -> ManagedValue {
         ManagedValue elt =
            SGF.B.createTupleElementAddr(loc, value, i, fieldType);
         if (!fieldType.isAddressOnly(SGF.F)) {
            return SGF.B.createLoadTake(loc, elt);
         }

         return SGF.emitManagedRValueWithCleanup(elt.getValue());
      });
}

void TupleInitialization::finishUninitialized(PILGenFunction &SGF) {
   for (auto &subInit : SubInitializations) {
      subInit->finishUninitialized(SGF);
   }
}

namespace {
class CleanupClosureConstant : public Cleanup {
   PILValue closure;
public:
   CleanupClosureConstant(PILValue closure) : closure(closure) {}
   void emit(PILGenFunction &SGF, CleanupLocation l,
             ForUnwind_t forUnwind) override {
      SGF.B.emitDestroyValueOperation(l, closure);
   }
   void dump(PILGenFunction &) const override {
#ifndef NDEBUG
      llvm::errs() << "CleanupClosureConstant\n"
                   << "State:" << getState() << "\n"
                   << "closure:" << closure << "\n";
#endif
   }
};
} // end anonymous namespace

SubstitutionMap PILGenFunction::getForwardingSubstitutionMap() {
   return F.getForwardingSubstitutionMap();
}

void PILGenFunction::visitFuncDecl(FuncDecl *fd) {
   // Generate the local function body.
   SGM.emitFunction(fd);
}

MutableArrayRef<InitializationPtr>
SingleBufferInitialization::
splitIntoTupleElements(PILGenFunction &SGF, PILLocation loc, CanType type,
                       SmallVectorImpl<InitializationPtr> &buf) {
   assert(SplitCleanups.empty() && "getting sub-initializations twice?");
   auto address = getAddressForInPlaceInitialization(SGF, loc);
   return splitSingleBufferIntoTupleElements(SGF, loc, type, address,
                                             buf, SplitCleanups);
}

MutableArrayRef<InitializationPtr>
SingleBufferInitialization::
splitSingleBufferIntoTupleElements(PILGenFunction &SGF, PILLocation loc,
                                   CanType type, PILValue baseAddr,
                                   SmallVectorImpl<InitializationPtr> &buf,
                                   TinyPtrVector<CleanupHandle::AsPointer> &splitCleanups) {
   // Destructure the buffer into per-element buffers.
   for (auto i : indices(cast<TupleType>(type)->getElementTypes())) {
      // Project the element.
      PILValue eltAddr = SGF.B.createTupleElementAddr(loc, baseAddr, i);

      // Create an initialization to initialize the element.
      auto &eltTL = SGF.getTypeLowering(eltAddr->getType());
      auto eltInit = SGF.useBufferAsTemporary(eltAddr, eltTL);

      // Remember the element cleanup.
      auto eltCleanup = eltInit->getInitializedCleanup();
      if (eltCleanup.isValid())
         splitCleanups.push_back(eltCleanup);

      buf.emplace_back(eltInit.release());
   }

   return buf;
}

void SingleBufferInitialization::
copyOrInitValueIntoSingleBuffer(PILGenFunction &SGF, PILLocation loc,
                                ManagedValue value, bool isInit,
                                PILValue destAddr) {
   // Emit an unchecked access around initialization of the local buffer to
   // silence access marker verification.
   //
   // FIXME: This is not a good place for FormalEvaluationScope +
   // UnenforcedFormalAccess.  However, there's no way to identify the buffer
   // initialization sequence after PILGen, and no easy way to wrap the
   // Initialization in an access during top-level expression evaluation.
   FormalEvaluationScope scope(SGF);
   if (!isInit) {
      assert(value.getValue() != destAddr && "copying in place?!");
      PILValue accessAddr =
         UnenforcedFormalAccess::enter(SGF, loc, destAddr, PILAccessKind::Modify);
      value.copyInto(SGF, loc, accessAddr);
      return;
   }

   // If we didn't evaluate into the initialization buffer, do so now.
   if (value.getValue() != destAddr) {
      PILValue accessAddr =
         UnenforcedFormalAccess::enter(SGF, loc, destAddr, PILAccessKind::Modify);
      value.forwardInto(SGF, loc, accessAddr);
   } else {
      // If we did evaluate into the initialization buffer, disable the
      // cleanup.
      value.forwardCleanup(SGF);
   }
}

void SingleBufferInitialization::finishInitialization(PILGenFunction &SGF) {
   // Forward all of the split element cleanups, assuming we made any.
   for (CleanupHandle eltCleanup : SplitCleanups)
      SGF.Cleanups.forwardCleanup(eltCleanup);
}

bool KnownAddressInitialization::isInPlaceInitializationOfGlobal() const {
   return isa<GlobalAddrInst>(address);
}

bool TemporaryInitialization::isInPlaceInitializationOfGlobal() const {
   return isa<GlobalAddrInst>(Addr);
}

void TemporaryInitialization::finishInitialization(PILGenFunction &SGF) {
   SingleBufferInitialization::finishInitialization(SGF);
   if (Cleanup.isValid())
      SGF.Cleanups.setCleanupState(Cleanup, CleanupState::Active);
}

namespace {
class ReleaseValueCleanup : public Cleanup {
   PILValue v;
public:
   ReleaseValueCleanup(PILValue v) : v(v) {}

   void emit(PILGenFunction &SGF, CleanupLocation l,
             ForUnwind_t forUnwind) override {
      if (v->getType().isAddress())
         SGF.B.createDestroyAddr(l, v);
      else
         SGF.B.emitDestroyValueOperation(l, v);
   }

   void dump(PILGenFunction &) const override {
#ifndef NDEBUG
      llvm::errs() << "ReleaseValueCleanup\n"
                   << "State:" << getState() << "\n"
                   << "Value:" << v << "\n";
#endif
   }
};
} // end anonymous namespace

namespace {
/// Cleanup to destroy an initialized variable.
class DeallocStackCleanup : public Cleanup {
   PILValue Addr;
public:
   DeallocStackCleanup(PILValue addr) : Addr(addr) {}

   void emit(PILGenFunction &SGF, CleanupLocation l,
             ForUnwind_t forUnwind) override {
      SGF.B.createDeallocStack(l, Addr);
   }

   void dump(PILGenFunction &) const override {
#ifndef NDEBUG
      llvm::errs() << "DeallocStackCleanup\n"
                   << "State:" << getState() << "\n"
                   << "Addr:" << Addr << "\n";
#endif
   }
};
} // end anonymous namespace

namespace {
/// Cleanup to destroy an initialized 'var' variable.
class DestroyLocalVariable : public Cleanup {
   VarDecl *Var;
public:
   DestroyLocalVariable(VarDecl *var) : Var(var) {}

   void emit(PILGenFunction &SGF, CleanupLocation l,
             ForUnwind_t forUnwind) override {
      SGF.destroyLocalVariable(l, Var);
   }

   void dump(PILGenFunction &SGF) const override {
#ifndef NDEBUG
      llvm::errs() << "DestroyLocalVariable\n"
                   << "State:" << getState() << "\n"
                   << "Decl: ";
      Var->print(llvm::errs());
      llvm::errs() << "\n";
      if (isActive()) {
         auto loc = SGF.VarLocs[Var];
         assert((loc.box || loc.value) && "One of box or value should be set");
         if (loc.box) {
            llvm::errs() << "Box: " << loc.box << "\n";
         } else {
            llvm::errs() << "Value: " << loc.value << "\n";
         }
      }
      llvm::errs() << "\n";
#endif
   }
};
} // end anonymous namespace

namespace {
/// Cleanup to destroy an uninitialized local variable.
class DeallocateUninitializedLocalVariable : public Cleanup {
   VarDecl *Var;
public:
   DeallocateUninitializedLocalVariable(VarDecl *var) : Var(var) {}

   void emit(PILGenFunction &SGF, CleanupLocation l,
             ForUnwind_t forUnwind) override {
      SGF.deallocateUninitializedLocalVariable(l, Var);
   }

   void dump(PILGenFunction &) const override {
#ifndef NDEBUG
      llvm::errs() << "DeallocateUninitializedLocalVariable\n"
                   << "State:" << getState() << "\n";
      // TODO: Make sure we dump var.
      llvm::errs() << "\n";
#endif
   }
};
} // end anonymous namespace

namespace {
/// An initialization of a local 'var'.
class LocalVariableInitialization : public SingleBufferInitialization {
   /// The local variable decl being initialized.
   VarDecl *decl;
   PILGenFunction &SGF;

   /// The cleanup we pushed to deallocate the local variable before it
   /// gets initialized.
   CleanupHandle DeallocCleanup;

   /// The cleanup we pushed to destroy and deallocate the local variable.
   CleanupHandle ReleaseCleanup;

   bool DidFinish = false;
public:
   /// Sets up an initialization for the allocated box. This pushes a
   /// CleanupUninitializedBox cleanup that will be replaced when
   /// initialization is completed.
   LocalVariableInitialization(VarDecl *decl,
                               Optional<MarkUninitializedInst::Kind> kind,
                               uint16_t ArgNo, PILGenFunction &SGF)
      : decl(decl), SGF(SGF) {
      assert(decl->getDeclContext()->isLocalContext() &&
             "can't emit a local var for a non-local var decl");
      assert(decl->hasStorage() && "can't emit storage for a computed variable");
      assert(!SGF.VarLocs.count(decl) && "Already have an entry for this decl?");
      // The box type's context is lowered in the minimal resilience domain.
      auto boxType = SGF.SGM.Types.getContextBoxTypeForCapture(
         decl,
         SGF.SGM.Types.getLoweredRValueType(TypeExpansionContext::minimal(),
                                            decl->getType()),
         SGF.F.getGenericEnvironment(),
         /*mutable*/ true);

      // The variable may have its lifetime extended by a closure, heap-allocate
      // it using a box.
      PILDebugVariable DbgVar(decl->isLet(), ArgNo);
      PILValue allocBox = SGF.B.createAllocBox(decl, boxType, DbgVar);

      // Mark the memory as uninitialized, so DI will track it for us.
      if (kind)
         allocBox = SGF.B.createMarkUninitialized(decl, allocBox, kind.getValue());

      PILValue addr = SGF.B.createProjectBox(decl, allocBox, 0);

      /// Remember that this is the memory location that we're emitting the
      /// decl to.
      SGF.VarLocs[decl] = PILGenFunction::VarLoc::get(addr, allocBox);

      // Push a cleanup to destroy the local variable.  This has to be
      // inactive until the variable is initialized.
      SGF.Cleanups.pushCleanupInState<DestroyLocalVariable>(CleanupState::Dormant,
                                                            decl);
      ReleaseCleanup = SGF.Cleanups.getTopCleanup();

      // Push a cleanup to deallocate the local variable.
      SGF.Cleanups.pushCleanup<DeallocateUninitializedLocalVariable>(decl);
      DeallocCleanup = SGF.Cleanups.getTopCleanup();
   }

   ~LocalVariableInitialization() override {
      assert(DidFinish && "did not call VarInit::finishInitialization!");
   }

   PILValue getAddress() const {
      assert(SGF.VarLocs.count(decl) && "did not emit var?!");
      return SGF.VarLocs[decl].value;
   }

   PILValue getAddressForInPlaceInitialization(PILGenFunction &SGF,
                                               PILLocation loc) override {
      return getAddress();
   }

   bool isInPlaceInitializationOfGlobal() const override {
      return isa<GlobalAddrInst>(getAddress());
   }

   void finishUninitialized(PILGenFunction &SGF) override {
      LocalVariableInitialization::finishInitialization(SGF);
   }

   void finishInitialization(PILGenFunction &SGF) override {
      SingleBufferInitialization::finishInitialization(SGF);
      assert(!DidFinish &&
             "called LocalVariableInitialization::finishInitialization twice!");
      SGF.Cleanups.setCleanupState(DeallocCleanup, CleanupState::Dead);
      SGF.Cleanups.setCleanupState(ReleaseCleanup, CleanupState::Active);
      DidFinish = true;
   }
};
} // end anonymous namespace

namespace {
/// Initialize a writeback buffer that receives the value of a 'let'
/// declaration.
class LetValueInitialization : public Initialization {
   /// The VarDecl for the let decl.
   VarDecl *vd;

   /// The address of the buffer used for the binding, if this is an address-only
   /// let.
   PILValue address;

   /// The cleanup we pushed to destroy the local variable.
   CleanupHandle DestroyCleanup;

   /// Cleanups we introduced when splitting.
   TinyPtrVector<CleanupHandle::AsPointer> SplitCleanups;

   bool DidFinish = false;

public:
   LetValueInitialization(VarDecl *vd, PILGenFunction &SGF) : vd(vd) {
      auto &lowering = SGF.getTypeLowering(vd->getType());

      // Decide whether we need a temporary stack buffer to evaluate this 'let'.
      // There are three cases we need to handle here: parameters, initialized (or
      // bound) decls, and uninitialized ones.
      bool needsTemporaryBuffer;
      bool isUninitialized = false;

      assert(!isa<ParamDecl>(vd)
             && "should not bind function params on this path");
      if (vd->getParentPatternBinding() && !vd->getParentInitializer()) {
         // This value is uninitialized (and unbound) if it has a pattern binding
         // decl, with no initializer value.
         assert(!vd->hasNonPatternBindingInit() && "Bound values aren't uninit!");

         // If this is a let-value without an initializer, then we need a temporary
         // buffer.  DI will make sure it is only assigned to once.
         needsTemporaryBuffer = true;
         isUninitialized = true;
      } else {
         // If this is a let with an initializer or bound value, we only need a
         // buffer if the type is address only.
         needsTemporaryBuffer =
            lowering.isAddressOnly() && SGF.silConv.useLoweredAddresses();
      }

      if (needsTemporaryBuffer) {
         address = SGF.emitTemporaryAllocation(vd, lowering.getLoweredType());
         if (isUninitialized)
            address = SGF.B.createMarkUninitializedVar(vd, address);
         DestroyCleanup = SGF.enterDormantTemporaryCleanup(address, lowering);
         SGF.VarLocs[vd] = PILGenFunction::VarLoc::get(address);
      } else if (!lowering.isTrivial()) {
         // Push a cleanup to destroy the let declaration.  This has to be
         // inactive until the variable is initialized: if control flow exits the
         // before the value is bound, we don't want to destroy the value.
         SGF.Cleanups.pushCleanupInState<DestroyLocalVariable>(
            CleanupState::Dormant, vd);
         DestroyCleanup = SGF.Cleanups.getTopCleanup();
      } else {
         DestroyCleanup = CleanupHandle::invalid();
      }
   }

   ~LetValueInitialization() override {
      assert(DidFinish && "did not call LetValueInit::finishInitialization!");
   }

   bool hasAddress() const { return (bool)address; }

   bool canPerformInPlaceInitialization() const override {
      return hasAddress();
   }

   bool isInPlaceInitializationOfGlobal() const override {
      return isa<GlobalAddrInst>(address);
   }

   PILValue getAddressForInPlaceInitialization(PILGenFunction &SGF,
                                               PILLocation loc) override {
      // Emit into the buffer that 'let's produce for address-only values if
      // we have it.
      assert(hasAddress());
      return address;
   }

   /// Return true if we can get the addresses of elements with the
   /// 'getSubInitializationsForTuple' method.
   ///
   /// Let-value initializations cannot be broken into constituent pieces if a
   /// scalar value needs to be bound.  If there is an address in play, then we
   /// can initialize the address elements of the tuple though.
   bool canSplitIntoTupleElements() const override {
      return hasAddress();
   }

   MutableArrayRef<InitializationPtr>
   splitIntoTupleElements(PILGenFunction &SGF, PILLocation loc, CanType type,
                          SmallVectorImpl<InitializationPtr> &buf) override {
      assert(SplitCleanups.empty());
      auto address = getAddressForInPlaceInitialization(SGF, loc);
      return SingleBufferInitialization
      ::splitSingleBufferIntoTupleElements(SGF, loc, type, address, buf,
                                           SplitCleanups);
   }

   void bindValue(PILValue value, PILGenFunction &SGF) {
      assert(!SGF.VarLocs.count(vd) && "Already emitted this vardecl?");
      // If we're binding an address to this let value, then we can use it as an
      // address later.  This happens when binding an address only parameter to
      // an argument, for example.
      if (value->getType().isAddress())
         address = value;
      SGF.VarLocs[vd] = PILGenFunction::VarLoc::get(value);

      // Emit a debug_value[_addr] instruction to record the start of this value's
      // lifetime.
      PILLocation PrologueLoc(vd);
      PrologueLoc.markAsPrologue();
      PILDebugVariable DbgVar(vd->isLet(), /*ArgNo=*/0);
      if (address)
         SGF.B.createDebugValueAddr(PrologueLoc, value, DbgVar);
      else
         SGF.B.createDebugValue(PrologueLoc, value, DbgVar);
   }

   void copyOrInitValueInto(PILGenFunction &SGF, PILLocation loc,
                            ManagedValue value, bool isInit) override {
      // If this let value has an address, we can handle it just like a single
      // buffer value.
      if (hasAddress())
         return SingleBufferInitialization::
         copyOrInitValueIntoSingleBuffer(SGF, loc, value, isInit, address);

      // Otherwise, we bind the value.
      if (isInit) {
         // Disable the rvalue expression cleanup, since the let value
         // initialization has a cleanup that lives for the entire scope of the
         // let declaration.
         bindValue(value.forward(SGF), SGF);
      } else {
         // Disable the expression cleanup of the copy, since the let value
         // initialization has a cleanup that lives for the entire scope of the
         // let declaration.
         bindValue(value.copyUnmanaged(SGF, loc).forward(SGF), SGF);
      }
   }

   void finishUninitialized(PILGenFunction &SGF) override {
      LetValueInitialization::finishInitialization(SGF);
   }

   void finishInitialization(PILGenFunction &SGF) override {
      assert(!DidFinish &&
             "called LetValueInit::finishInitialization twice!");
      assert(SGF.VarLocs.count(vd) && "Didn't bind a value to this let!");

      // Deactivate any cleanups we made when splitting the tuple.
      for (auto cleanup : SplitCleanups)
         SGF.Cleanups.forwardCleanup(cleanup);

      // Activate the destroy cleanup.
      if (DestroyCleanup != CleanupHandle::invalid())
         SGF.Cleanups.setCleanupState(DestroyCleanup, CleanupState::Active);

      DidFinish = true;
   }
};
} // end anonymous namespace


namespace {
/// Initialize a variable of reference-storage type.
class ReferenceStorageInitialization : public Initialization {
   InitializationPtr VarInit;
public:
   ReferenceStorageInitialization(InitializationPtr &&subInit)
      : VarInit(std::move(subInit)) {
      assert(VarInit->canPerformInPlaceInitialization());
   }

   void copyOrInitValueInto(PILGenFunction &SGF, PILLocation loc,
                            ManagedValue value, bool isInit) override {
      auto address = VarInit->getAddressForInPlaceInitialization(SGF, loc);
      // If this is not an initialization, copy the value before we translateIt,
      // translation expects a +1 value.
      if (isInit)
         value.forwardInto(SGF, loc, address);
      else
         value.copyInto(SGF, loc, address);
   }

   void finishUninitialized(PILGenFunction &SGF) override {
      ReferenceStorageInitialization::finishInitialization(SGF);
   }

   void finishInitialization(PILGenFunction &SGF) override {
      VarInit->finishInitialization(SGF);
   }
};
} // end anonymous namespace

namespace {
/// Abstract base class for refutable pattern initializations.
class RefutablePatternInitialization : public Initialization {
   /// This is the label to jump to if the pattern fails to match.
   JumpDest failureDest;
public:
   RefutablePatternInitialization(JumpDest failureDest)
      : failureDest(failureDest) {
      assert(failureDest.isValid() &&
             "Refutable patterns can only exist in failable conditions");
   }

   JumpDest getFailureDest() const { return failureDest; }

   void copyOrInitValueInto(PILGenFunction &SGF, PILLocation loc,
                            ManagedValue value, bool isInit) override = 0;

   void bindVariable(PILLocation loc, VarDecl *var, ManagedValue value,
                     CanType formalValueType, PILGenFunction &SGF) {
      // Initialize the variable value.
      InitializationPtr init = SGF.emitInitializationForVarDecl(var, var->isLet());
      RValue(SGF, loc, formalValueType, value).forwardInto(SGF, loc, init.get());
   }

};
} // end anonymous namespace

namespace {
class ExprPatternInitialization : public RefutablePatternInitialization {
   ExprPattern *P;
public:
   ExprPatternInitialization(ExprPattern *P, JumpDest patternFailDest)
      : RefutablePatternInitialization(patternFailDest), P(P) {}

   void copyOrInitValueInto(PILGenFunction &SGF, PILLocation loc,
                            ManagedValue value, bool isInit) override;
};
} // end anonymous namespace

void ExprPatternInitialization::
copyOrInitValueInto(PILGenFunction &SGF, PILLocation loc,
                    ManagedValue value, bool isInit) {
   assert(isInit && "Only initialization is supported for refutable patterns");

   FullExpr scope(SGF.Cleanups, CleanupLocation(P));
   bindVariable(P, P->getMatchVar(), value,
                P->getType()->getCanonicalType(), SGF);

   // Emit the match test.
   PILValue testBool;
   {
      FullExpr scope(SGF.Cleanups, CleanupLocation(P->getMatchExpr()));
      testBool = SGF.emitRValueAsSingleValue(P->getMatchExpr()).
         getUnmanagedValue();
   }

   assert(testBool->getType().getAstType()->isBool());
   auto i1Value = SGF.emitUnwrapIntegerResult(loc, testBool);

   PILBasicBlock *contBB = SGF.B.splitBlockForFallthrough();
   auto falseBB = SGF.Cleanups.emitBlockForCleanups(getFailureDest(), loc);
   SGF.B.createCondBranch(loc, i1Value, contBB, falseBB);

   SGF.B.setInsertionPoint(contBB);
}

namespace {
class EnumElementPatternInitialization : public RefutablePatternInitialization {
   EnumElementDecl *ElementDecl;
   InitializationPtr subInitialization;
public:
   EnumElementPatternInitialization(EnumElementDecl *ElementDecl,
                                    InitializationPtr &&subInitialization,
                                    JumpDest patternFailDest)
      : RefutablePatternInitialization(patternFailDest), ElementDecl(ElementDecl),
        subInitialization(std::move(subInitialization)) {}

   void copyOrInitValueInto(PILGenFunction &SGF, PILLocation loc,
                            ManagedValue value, bool isInit) override {
      assert(isInit && "Only initialization is supported for refutable patterns");
      emitEnumMatch(value, ElementDecl, subInitialization.get(), getFailureDest(),
                    loc, SGF);
   }

   static void emitEnumMatch(ManagedValue value, EnumElementDecl *ElementDecl,
                             Initialization *subInit, JumpDest FailureDest,
                             PILLocation loc, PILGenFunction &SGF);

   void finishInitialization(PILGenFunction &SGF) override {
      if (subInitialization.get())
         subInitialization->finishInitialization(SGF);
   }
};
} // end anonymous namespace

/// If \p elt belongs to an enum that has exactly two cases and that can be
/// exhaustively switched, return the other case. Otherwise, return nullptr.
static EnumElementDecl *getOppositeBinaryDecl(const PILGenFunction &SGF,
                                              const EnumElementDecl *elt) {
   const EnumDecl *enumDecl = elt->getParentEnum();
   if (!enumDecl->isEffectivelyExhaustive(SGF.SGM.PolarphpModule,
                                          SGF.F.getResilienceExpansion())) {
      return nullptr;
   }

   EnumDecl::ElementRange range = enumDecl->getAllElements();
   auto iter = range.begin();
   if (iter == range.end())
      return nullptr;
   bool seenDecl = false;
   EnumElementDecl *result = nullptr;
   if (*iter == elt) {
      seenDecl = true;
   } else {
      result = *iter;
   }

   ++iter;
   if (iter == range.end())
      return nullptr;
   if (seenDecl) {
      assert(!result);
      result = *iter;
   } else {
      if (elt != *iter)
         return nullptr;
      seenDecl = true;
   }
   ++iter;

   // If we reach this point, we saw the decl we were looking for and one other
   // case. If we have any additional cases, then we do not have a binary enum.
   if (iter != range.end())
      return nullptr;

   // This is always true since we have already returned earlier nullptr if we
   // did not see the decl at all.
   assert(seenDecl);
   return result;
}

void EnumElementPatternInitialization::emitEnumMatch(
   ManagedValue value, EnumElementDecl *eltDecl, Initialization *subInit,
   JumpDest failureDest, PILLocation loc, PILGenFunction &SGF) {

   // Create all of the blocks early so we can maintain a consistent ordering
   // (and update less tests). Break this at your fingers parallel.
   //
   // *NOTE* This needs to be in reverse order to preserve the textual PIL.
   auto *contBlock = SGF.createBasicBlock();
   auto *someBlock = SGF.createBasicBlock();
   auto *defaultBlock = SGF.createBasicBlock();
   auto *originalBlock = SGF.B.getInsertionBB();

   SwitchEnumBuilder switchBuilder(SGF.B, loc, value);

   // Handle the none case.
   //
   // *NOTE*: Since we are performing an initialization here, it is *VERY*
   // important that we emit the negative case first. The reason why is that
   // currently the initialization has a dormant cleanup in a scope that may be
   // after the failureDest depth. Once we run the positive case, this
   // initialization will be enabled. Thus if we run the negative case /after/
   // the positive case, a cleanup will be emitted for the initialization on the
   // negative path... but the actual initialization happened on the positive
   // path, causing a use (the destroy on the negative path) to be created that
   // does not dominate its definition (in the positive path).
   auto handler = [&SGF, &loc, &failureDest](ManagedValue mv,
                                             SwitchCaseFullExpr &&expr) {
      expr.exit();
      SGF.Cleanups.emitBranchAndCleanups(failureDest, loc);
   };

   // If we have a binary enum, do not emit a true default case. This ensures
   // that we do not emit a destroy_value on a .None.
   bool inferredBinaryEnum = false;
   if (auto *otherDecl = getOppositeBinaryDecl(SGF, eltDecl)) {
      inferredBinaryEnum = true;
      switchBuilder.addCase(otherDecl, defaultBlock, nullptr, handler);
   } else {
      switchBuilder.addDefaultCase(
         defaultBlock, nullptr, handler,
         SwitchEnumBuilder::DefaultDispatchTime::BeforeNormalCases);
   }

   // Always insert the some case at the front of the list. In the default case,
   // this will not matter, but in the case where we have a binary enum, we want
   // to preserve the old ordering of .some/.none. to make it easier to update
   // tests.
   switchBuilder.addCase(
      eltDecl, someBlock, contBlock,
      [&SGF, &loc, &eltDecl, &subInit, &value](ManagedValue mv,
                                               SwitchCaseFullExpr &&expr) {
         // If the enum case has no bound value, we're done.
         if (!eltDecl->hasAssociatedValues()) {
            assert(
               subInit == nullptr &&
               "Cannot have a subinit when there is no value to match against");
            expr.exitAndBranch(loc);
            return;
         }

         if (subInit == nullptr) {
            // If there is no subinitialization, then we are done matching.  Don't
            // bother projecting out the any elements value only to ignore it.
            expr.exitAndBranch(loc);
            return;
         }

         // Otherwise, the bound value for the enum case is available.
         PILType eltTy = value.getType().getEnumElementType(
            eltDecl, SGF.SGM.M, SGF.getTypeExpansionContext());
         auto &eltTL = SGF.getTypeLowering(eltTy);

         if (mv.getType().isAddress()) {
            // If the enum is address-only, take from the enum we have and load it
            // if
            // the element value is loadable.
            assert((eltTL.isTrivial() || mv.hasCleanup()) &&
                   "must be able to consume value");
            mv = SGF.B.createUncheckedTakeEnumDataAddr(loc, mv, eltDecl, eltTy);
            // Load a loadable data value.
            if (eltTL.isLoadable())
               mv = SGF.B.createLoadTake(loc, mv);
         }

         // If the payload is indirect, project it out of the box.
         if (eltDecl->isIndirect() || eltDecl->getParentEnum()->isIndirect()) {
            ManagedValue boxedValue = SGF.B.createProjectBox(loc, mv, 0);
            auto &boxedTL = SGF.getTypeLowering(boxedValue.getType());

            // We must treat the boxed value as +0 since it may be shared. Copy it
            // if nontrivial.
            //
            // NOTE: The APIs that we are usinng here will ensure that if we have
            // a trivial value, the load_borrow will become a load [trivial] and
            // the copies will be "automagically" elided.
            if (boxedTL.isLoadable() || !SGF.silConv.useLoweredAddresses()) {
               UnenforcedAccess access;
               PILValue accessAddress = access.beginAccess(
                  SGF, loc, boxedValue.getValue(), PILAccessKind::Read);
               auto mvAccessAddress = ManagedValue::forUnmanaged(accessAddress);
               {
                  Scope loadScope(SGF, loc);
                  ManagedValue borrowedVal =
                     SGF.B.createLoadBorrow(loc, mvAccessAddress);
                  mv = loadScope.popPreservingValue(
                     borrowedVal.copyUnmanaged(SGF, loc));
               }
               access.endAccess(SGF);
            } else {
               // If we do not have a loadable value, just do a copy of the
               // boxedValue.
               mv = boxedValue.copyUnmanaged(SGF, loc);
            }
         }

         // Reabstract to the substituted type, if needed.
         CanType substEltTy =
            value.getType()
               .getAstType()
               ->getTypeOfMember(SGF.SGM.M.getPolarphpModule(), eltDecl,
                                 eltDecl->getArgumentInterfaceType())
               ->getCanonicalType();

         AbstractionPattern origEltTy =
            (eltDecl == SGF.getAstContext().getOptionalSomeDecl()
             ? AbstractionPattern(substEltTy)
             : SGF.SGM.M.Types.getAbstractionPattern(eltDecl));

         mv = SGF.emitOrigToSubstValue(loc, mv, origEltTy, substEltTy);

         // Pass the +1 value down into the sub initialization.
         subInit->copyOrInitValueInto(SGF, loc, mv, /*is an init*/ true);
         expr.exitAndBranch(loc);
      });

   std::move(switchBuilder).emit();

   // If we inferred a binary enum, put the asked for case first so we preserve
   // the current code structure. This just ensures that less test updates are
   // needed.
   if (inferredBinaryEnum) {
      if (auto *switchEnum =
         dyn_cast<SwitchEnumInst>(originalBlock->getTerminator())) {
         switchEnum->swapCase(0, 1);
      } else {
         auto *switchEnumAddr =
            cast<SwitchEnumAddrInst>(originalBlock->getTerminator());
         switchEnumAddr->swapCase(0, 1);
      }
   }

   // Reset the insertion point to the end of contBlock.
   SGF.B.setInsertionPoint(contBlock);
}

namespace {
class IsPatternInitialization : public RefutablePatternInitialization {
   IsPattern *pattern;
   InitializationPtr subInitialization;
public:
   IsPatternInitialization(IsPattern *pattern,
                           InitializationPtr &&subInitialization,
                           JumpDest patternFailDest)
      : RefutablePatternInitialization(patternFailDest), pattern(pattern),
        subInitialization(std::move(subInitialization)) {}

   void copyOrInitValueInto(PILGenFunction &SGF, PILLocation loc,
                            ManagedValue value, bool isInit) override;

   void finishInitialization(PILGenFunction &SGF) override {
      if (subInitialization.get())
         subInitialization->finishInitialization(SGF);
   }
};
} // end anonymous namespace

void IsPatternInitialization::
copyOrInitValueInto(PILGenFunction &SGF, PILLocation loc,
                    ManagedValue value, bool isInit) {
   assert(isInit && "Only initialization is supported for refutable patterns");

   // Try to perform the cast to the destination type, producing an optional that
   // indicates whether we succeeded.
   auto destType = OptionalType::get(pattern->getCastTypeLoc().getType());

   value =
      emitConditionalCheckedCast(SGF, loc, value, pattern->getType(), destType,
                                 pattern->getCastKind(), SGFContext(),
                                 ProfileCounter(), ProfileCounter())
         .getAsSingleValue(SGF, loc);

   // Now that we have our result as an optional, we can use an enum projection
   // to do all the work.
   EnumElementPatternInitialization::
   emitEnumMatch(value, SGF.getAstContext().getOptionalSomeDecl(),
                 subInitialization.get(), getFailureDest(), loc, SGF);
}

namespace {
class BoolPatternInitialization : public RefutablePatternInitialization {
   BoolPattern *pattern;
public:
   BoolPatternInitialization(BoolPattern *pattern,
                             JumpDest patternFailDest)
      : RefutablePatternInitialization(patternFailDest), pattern(pattern) {}

   void copyOrInitValueInto(PILGenFunction &SGF, PILLocation loc,
                            ManagedValue value, bool isInit) override;
};
} // end anonymous namespace

void BoolPatternInitialization::
copyOrInitValueInto(PILGenFunction &SGF, PILLocation loc,
                    ManagedValue value, bool isInit) {
   assert(isInit && "Only initialization is supported for refutable patterns");

   // Extract the i1 from the Bool struct.
   auto i1Value = SGF.emitUnwrapIntegerResult(loc, value.forward(SGF));

   // Branch on the boolean based on whether we're testing for true or false.
   PILBasicBlock *trueBB = SGF.B.splitBlockForFallthrough();
   auto contBB = trueBB;
   auto falseBB = SGF.Cleanups.emitBlockForCleanups(getFailureDest(), loc);

   if (!pattern->getValue())
      std::swap(trueBB, falseBB);
   SGF.B.createCondBranch(loc, i1Value, trueBB, falseBB);
   SGF.B.setInsertionPoint(contBB);
}


namespace {

/// InitializationForPattern - A visitor for traversing a pattern, generating
/// PIL code to allocate the declared variables, and generating an
/// Initialization representing the needed initializations.
///
/// It is important that any Initialization created for a pattern that might
/// not have an immediate initializer implement finishUninitialized.  Note
/// that this only applies to irrefutable patterns.
struct InitializationForPattern
   : public PatternVisitor<InitializationForPattern, InitializationPtr>
{
   PILGenFunction &SGF;

   /// This is the place that should be jumped to if the pattern fails to match.
   /// This is invalid for irrefutable pattern initializations.
   JumpDest patternFailDest;

   InitializationForPattern(PILGenFunction &SGF, JumpDest patternFailDest)
      : SGF(SGF), patternFailDest(patternFailDest) {}

   // Paren, Typed, and Var patterns are noops, just look through them.
   InitializationPtr visitParenPattern(ParenPattern *P) {
      return visit(P->getSubPattern());
   }
   InitializationPtr visitTypedPattern(TypedPattern *P) {
      return visit(P->getSubPattern());
   }
   InitializationPtr visitVarPattern(VarPattern *P) {
      return visit(P->getSubPattern());
   }

   // AnyPatterns (i.e, _) don't require any storage. Any value bound here will
   // just be dropped.
   InitializationPtr visitAnyPattern(AnyPattern *P) {
      return InitializationPtr(new BlackHoleInitialization());
   }

   // Bind to a named pattern by creating a memory location and initializing it
   // with the initial value.
   InitializationPtr visitNamedPattern(NamedPattern *P) {
      if (!P->getDecl()->hasName()) {
         // Unnamed parameters don't require any storage. Any value bound here will
         // just be dropped.
         return InitializationPtr(new BlackHoleInitialization());
      }

      return SGF.emitInitializationForVarDecl(P->getDecl(), P->getDecl()->isLet());
   }

   // Bind a tuple pattern by aggregating the component variables into a
   // TupleInitialization.
   InitializationPtr visitTuplePattern(TuplePattern *P) {
      TupleInitialization *init = new TupleInitialization();
      for (auto &elt : P->getElements())
         init->SubInitializations.push_back(visit(elt.getPattern()));
      return InitializationPtr(init);
   }

   InitializationPtr visitEnumElementPattern(EnumElementPattern *P) {
      InitializationPtr subInit;
      if (auto *subP = P->getSubPattern())
         subInit = visit(subP);
      auto *res = new EnumElementPatternInitialization(P->getElementDecl(),
                                                       std::move(subInit),
                                                       patternFailDest);
      return InitializationPtr(res);
   }
   InitializationPtr visitOptionalSomePattern(OptionalSomePattern *P) {
      InitializationPtr subInit = visit(P->getSubPattern());
      auto *res = new EnumElementPatternInitialization(P->getElementDecl(),
                                                       std::move(subInit),
                                                       patternFailDest);
      return InitializationPtr(res);
   }
   InitializationPtr visitIsPattern(IsPattern *P) {
      InitializationPtr subInit;
      if (auto *subP = P->getSubPattern())
         subInit = visit(subP);
      return InitializationPtr(new IsPatternInitialization(P, std::move(subInit),
                                                           patternFailDest));
   }
   InitializationPtr visitBoolPattern(BoolPattern *P) {
      return InitializationPtr(new BoolPatternInitialization(P, patternFailDest));
   }
   InitializationPtr visitExprPattern(ExprPattern *P) {
      return InitializationPtr(new ExprPatternInitialization(P, patternFailDest));
   }
};

} // end anonymous namespace

InitializationPtr
PILGenFunction::emitInitializationForVarDecl(VarDecl *vd, bool forceImmutable) {
   // If this is a computed variable, we don't need to do anything here.
   // We'll generate the getter and setter when we see their FuncDecls.
   if (!vd->hasStorage())
      return InitializationPtr(new BlackHoleInitialization());

   if (vd->isDebuggerVar()) {
      DebuggerClient *DebugClient = SGM.PolarphpModule->getDebugClient();
      assert(DebugClient && "Debugger variables with no debugger client");
      PILDebuggerClient *PILDebugClient = DebugClient->getAsPILDebuggerClient();
      assert(PILDebugClient && "Debugger client doesn't support PIL");
      PILValue SV = PILDebugClient->emitLValueForVariable(vd, B);

      VarLocs[vd] = PILGenFunction::VarLoc::get(SV);
      return InitializationPtr(new KnownAddressInitialization(SV));
   }

   CanType varType = vd->getType()->getCanonicalType();

   assert(!isa<InOutType>(varType) && "local variables should never be inout");

   // If this is a 'let' initialization for a non-global, set up a
   // let binding, which stores the initialization value into VarLocs directly.
   if (forceImmutable && vd->getDeclContext()->isLocalContext() &&
       !isa<ReferenceStorageType>(varType))
      return InitializationPtr(new LetValueInitialization(vd, *this));

   // If the variable has no initial value, emit a mark_uninitialized instruction
   // so that DI tracks and enforces validity of it.
   bool isUninitialized =
      vd->getParentPatternBinding() && !vd->getParentInitializer();

   // If this is a global variable, initialize it without allocations or
   // cleanups.
   InitializationPtr Result;
   if (!vd->getDeclContext()->isLocalContext()) {
      auto *silG = SGM.getPILGlobalVariable(vd, NotForDefinition);
      B.createAllocGlobal(vd, silG);
      PILValue addr = B.createGlobalAddr(vd, silG);
      if (isUninitialized)
         addr = B.createMarkUninitializedVar(vd, addr);

      VarLocs[vd] = PILGenFunction::VarLoc::get(addr);
      Result = InitializationPtr(new KnownAddressInitialization(addr));
   } else {
      Optional<MarkUninitializedInst::Kind> uninitKind;
      if (isUninitialized) {
         uninitKind = MarkUninitializedInst::Kind::Var;
      }
      Result = emitLocalVariableWithCleanup(vd, uninitKind);
   }

   // If we're initializing a weak or unowned variable, this requires a change in
   // type.
   if (isa<ReferenceStorageType>(varType))
      Result = InitializationPtr(new
                                    ReferenceStorageInitialization(std::move(Result)));
   return Result;
}

void PILGenFunction::emitPatternBinding(PatternBindingDecl *PBD,
                                        unsigned idx) {
   auto initialization = emitPatternBindingInitialization(PBD->getPattern(idx),
                                                          JumpDest::invalid());

   // If an initial value expression was specified by the decl, emit it into
   // the initialization. Otherwise, mark it uninitialized for DI to resolve.
   if (auto *Init = PBD->getExecutableInit(idx)) {
      FullExpr Scope(Cleanups, CleanupLocation(Init));
      emitExprInto(Init, initialization.get(), PILLocation(PBD));
   } else {
      initialization->finishUninitialized(*this);
   }
}

void PILGenFunction::visitPatternBindingDecl(PatternBindingDecl *PBD) {

   // Allocate the variables and build up an Initialization over their
   // allocated storage.
   for (unsigned i : range(PBD->getNumPatternEntries())) {
      emitPatternBinding(PBD, i);
   }
}

void PILGenFunction::visitVarDecl(VarDecl *D) {
   // We handle emitting the variable storage when we see the pattern binding.

   // Emit the variable's accessors.
   D->visitEmittedAccessors([&](AccessorDecl *accessor) {
      SGM.emitFunction(accessor);
   });
}

/// Emit literals for the major, minor, and subminor components of the version
/// and return a tuple of PILValues for them.
static std::tuple<PILValue, PILValue, PILValue>
emitVersionLiterals(PILLocation loc, PILGenBuilder &B, AstContext &ctx,
                    llvm::VersionTuple Vers) {
   unsigned major = Vers.getMajor();
   unsigned minor =
      (Vers.getMinor().hasValue() ? Vers.getMinor().getValue() : 0);
   unsigned subminor =
      (Vers.getSubminor().hasValue() ? Vers.getSubminor().getValue() : 0);

   PILType wordType = PILType::getBuiltinWordType(ctx);

   PILValue majorValue = B.createIntegerLiteral(loc, wordType, major);
   PILValue minorValue = B.createIntegerLiteral(loc, wordType, minor);
   PILValue subminorValue = B.createIntegerLiteral(loc, wordType, subminor);

   return std::make_tuple(majorValue, minorValue, subminorValue);
}

/// Emit a check that returns 1 if the running OS version is in
/// the specified version range and 0 otherwise. The returned PILValue
/// (which has type Builtin.Int1) represents the result of this check.
PILValue PILGenFunction::emitOSVersionRangeCheck(PILLocation loc,
                                                 const VersionRange &range) {
   // Emit constants for the checked version range.
   PILValue majorValue;
   PILValue minorValue;
   PILValue subminorValue;
   std::tie(majorValue, minorValue, subminorValue) =
      emitVersionLiterals(loc, B, getAstContext(), range.getLowerEndpoint());

   // Emit call to _stdlib_isOSVersionAtLeast(major, minor, patch)
   FuncDecl *versionQueryDecl =
      getAstContext().getIsOSVersionAtLeastDecl();
   assert(versionQueryDecl);

   auto silDeclRef = PILDeclRef(versionQueryDecl);
   PILValue availabilityGTEFn = emitGlobalFunctionRef(
      loc, silDeclRef, getConstantInfo(getTypeExpansionContext(), silDeclRef));

   PILValue args[] = {majorValue, minorValue, subminorValue};
   return B.createApply(loc, availabilityGTEFn, SubstitutionMap(), args);
}


/// Emit the boolean test and/or pattern bindings indicated by the specified
/// stmt condition.  If the condition fails, control flow is transferred to the
/// specified JumpDest.  The insertion point is left in the block where the
/// condition has matched and any bound variables are in scope.
///
void PILGenFunction::emitStmtCondition(StmtCondition Cond, JumpDest FalseDest,
                                       PILLocation loc,
                                       ProfileCounter NumTrueTaken,
                                       ProfileCounter NumFalseTaken) {

   assert(B.hasValidInsertionPoint() &&
          "emitting condition at unreachable point");

   for (const auto &elt : Cond) {
      PILLocation booleanTestLoc = loc;
      PILValue booleanTestValue;

      switch (elt.getKind()) {
         case StmtConditionElement::CK_PatternBinding: {
            InitializationPtr initialization =
               InitializationForPattern(*this, FalseDest).visit(elt.getPattern());

            // Emit the initial value into the initialization.
            FullExpr Scope(Cleanups, CleanupLocation(elt.getInitializer()));
            emitExprInto(elt.getInitializer(), initialization.get());
            // Pattern bindings handle their own tests, we don't need a boolean test.
            continue;
         }

         case StmtConditionElement::CK_Boolean: { // Handle boolean conditions.
            auto *expr = elt.getBoolean();
            // Evaluate the condition as an i1 value (guaranteed by Sema).
            FullExpr Scope(Cleanups, CleanupLocation(expr));
            booleanTestValue = emitRValue(expr).forwardAsSingleValue(*this, expr);
            booleanTestValue = emitUnwrapIntegerResult(expr, booleanTestValue);
            booleanTestLoc = expr;
            break;
         }
         case StmtConditionElement::CK_Availability:
            // Check the running OS version to determine whether it is in the range
            // specified by elt.
            VersionRange OSVersion = elt.getAvailability()->getAvailableRange();
            assert(!OSVersion.isEmpty());

            if (OSVersion.isAll()) {
               // If there's no check for the current platform, this condition is
               // trivially true.
               PILType i1 = PILType::getBuiltinIntegerType(1, getAstContext());
               booleanTestValue = B.createIntegerLiteral(loc, i1, true);
            } else {
               booleanTestValue = emitOSVersionRangeCheck(loc, OSVersion);
            }
            break;
      }

      // Now that we have a boolean test as a Builtin.i1, emit the branch.
      assert(booleanTestValue->getType().
         castTo<BuiltinIntegerType>()->isFixedWidth(1) &&
             "Sema forces conditions to have Builtin.i1 type");

      // Just branch on the condition.  On failure, we unwind any active cleanups,
      // on success we fall through to a new block.
      auto FailBB = Cleanups.emitBlockForCleanups(FalseDest, loc);
      PILBasicBlock *ContBB = createBasicBlock();
      B.createCondBranch(booleanTestLoc, booleanTestValue, ContBB, FailBB,
                         NumTrueTaken, NumFalseTaken);

      // Finally, emit the continue block and keep emitting the rest of the
      // condition.
      B.emitBlock(ContBB);
   }
}

InitializationPtr
PILGenFunction::emitPatternBindingInitialization(Pattern *P,
                                                 JumpDest failureDest) {
   return InitializationForPattern(*this, failureDest).visit(P);
}

/// Enter a cleanup to deallocate the given location.
CleanupHandle PILGenFunction::enterDeallocStackCleanup(PILValue temp) {
   assert(temp->getType().isAddress() &&  "dealloc must have an address type");
   Cleanups.pushCleanup<DeallocStackCleanup>(temp);
   return Cleanups.getTopCleanup();
}

CleanupHandle PILGenFunction::enterDestroyCleanup(PILValue valueOrAddr) {
   Cleanups.pushCleanup<ReleaseValueCleanup>(valueOrAddr);
   return Cleanups.getTopCleanup();
}

namespace {
/// A cleanup that deinitializes an opaque existential container
/// before a value has been stored into it, or after its value was taken.
class DeinitExistentialCleanup: public Cleanup {
   PILValue existentialAddr;
   CanType concreteFormalType;
   ExistentialRepresentation repr;
public:
   DeinitExistentialCleanup(PILValue existentialAddr,
                            CanType concreteFormalType,
                            ExistentialRepresentation repr)
      : existentialAddr(existentialAddr),
        concreteFormalType(concreteFormalType),
        repr(repr) {}

   void emit(PILGenFunction &SGF, CleanupLocation l,
             ForUnwind_t forUnwind) override {
      switch (repr) {
         case ExistentialRepresentation::None:
         case ExistentialRepresentation::Class:
         case ExistentialRepresentation::Metatype:
            llvm_unreachable("cannot cleanup existential");
         case ExistentialRepresentation::Opaque:
            if (SGF.silConv.useLoweredAddresses()) {
               SGF.B.createDeinitExistentialAddr(l, existentialAddr);
            } else {
               SGF.B.createDeinitExistentialValue(l, existentialAddr);
            }
            break;
         case ExistentialRepresentation::Boxed:
            auto box = SGF.B.createLoad(l, existentialAddr,
                                        LoadOwnershipQualifier::Take);
            SGF.B.createDeallocExistentialBox(l, concreteFormalType, box);
            break;
      }
   }

   void dump(PILGenFunction &) const override {
#ifndef NDEBUG
      llvm::errs() << "DeinitExistentialCleanup\n"
                   << "State:" << getState() << "\n"
                   << "Value:" << existentialAddr << "\n";
#endif
   }
};
} // end anonymous namespace

/// Enter a cleanup to emit a DeinitExistentialAddr or DeinitExistentialBox
/// of the specified value.
CleanupHandle PILGenFunction::enterDeinitExistentialCleanup(
   CleanupState state,
   PILValue addr,
   CanType concreteFormalType,
   ExistentialRepresentation repr) {
   assert(addr->getType().isAddress());
   Cleanups.pushCleanupInState<DeinitExistentialCleanup>(state, addr,
                                                         concreteFormalType, repr);
   return Cleanups.getTopCleanup();
}

/// Create a LocalVariableInitialization for the uninitialized var.
InitializationPtr PILGenFunction::emitLocalVariableWithCleanup(
   VarDecl *vd, Optional<MarkUninitializedInst::Kind> kind, unsigned ArgNo) {
   return InitializationPtr(
      new LocalVariableInitialization(vd, kind, ArgNo, *this));
}

/// Create an Initialization for an uninitialized temporary.
std::unique_ptr<TemporaryInitialization>
PILGenFunction::emitTemporary(PILLocation loc, const TypeLowering &tempTL) {
   PILValue addr = emitTemporaryAllocation(loc, tempTL.getLoweredType());
   return useBufferAsTemporary(addr, tempTL);
}

std::unique_ptr<TemporaryInitialization>
PILGenFunction::emitFormalAccessTemporary(PILLocation loc,
                                          const TypeLowering &tempTL) {
   PILValue addr = emitTemporaryAllocation(loc, tempTL.getLoweredType());
   CleanupHandle cleanup =
      enterDormantFormalAccessTemporaryCleanup(addr, loc, tempTL);
   return std::unique_ptr<TemporaryInitialization>(
      new TemporaryInitialization(addr, cleanup));
}

/// Create an Initialization for an uninitialized buffer.
std::unique_ptr<TemporaryInitialization>
PILGenFunction::useBufferAsTemporary(PILValue addr,
                                     const TypeLowering &tempTL) {
   CleanupHandle cleanup = enterDormantTemporaryCleanup(addr, tempTL);
   return std::unique_ptr<TemporaryInitialization>(
      new TemporaryInitialization(addr, cleanup));
}

CleanupHandle
PILGenFunction::enterDormantTemporaryCleanup(PILValue addr,
                                             const TypeLowering &tempTL) {
   if (tempTL.isTrivial())
      return CleanupHandle::invalid();

   Cleanups.pushCleanupInState<ReleaseValueCleanup>(CleanupState::Dormant, addr);
   return Cleanups.getCleanupsDepth();
}

namespace {

struct FormalAccessReleaseValueCleanup : Cleanup {
   FormalEvaluationContext::stable_iterator Depth;

   FormalAccessReleaseValueCleanup() : Depth() {}

   void setState(PILGenFunction &SGF, CleanupState newState) override {
      if (newState == CleanupState::Dead) {
         getEvaluation(SGF).setFinished();
      }

      Cleanup::setState(SGF, newState);
   }

   void emit(PILGenFunction &SGF, CleanupLocation l,
             ForUnwind_t forUnwind) override {
      getEvaluation(SGF).finish(SGF);
   }

   void dump(PILGenFunction &SGF) const override {
#ifndef NDEBUG
      llvm::errs() << "FormalAccessReleaseValueCleanup "
                   << "State:" << getState() << "\n"
                   << "Value:" << getValue(SGF) << "\n";
#endif
   }

   OwnedFormalAccess &getEvaluation(PILGenFunction &SGF) const {
      auto &evaluation = *SGF.FormalEvalContext.find(Depth);
      assert(evaluation.getKind() == FormalAccess::Owned);
      return static_cast<OwnedFormalAccess &>(evaluation);
   }

   PILValue getValue(PILGenFunction &SGF) const {
      return getEvaluation(SGF).getValue();
   }
};

} // end anonymous namespace

ManagedValue
PILGenFunction::emitFormalAccessManagedBufferWithCleanup(PILLocation loc,
                                                         PILValue addr) {
   assert(isInFormalEvaluationScope() && "Must be in formal evaluation scope");
   auto &lowering = getTypeLowering(addr->getType());
   if (lowering.isTrivial())
      return ManagedValue::forUnmanaged(addr);

   auto &cleanup = Cleanups.pushCleanup<FormalAccessReleaseValueCleanup>();
   CleanupHandle handle = Cleanups.getTopCleanup();
   FormalEvalContext.push<OwnedFormalAccess>(loc, handle, addr);
   cleanup.Depth = FormalEvalContext.stable_begin();
   return ManagedValue(addr, handle);
}

ManagedValue
PILGenFunction::emitFormalAccessManagedRValueWithCleanup(PILLocation loc,
                                                         PILValue value) {
   assert(isInFormalEvaluationScope() && "Must be in formal evaluation scope");
   auto &lowering = getTypeLowering(value->getType());
   if (lowering.isTrivial())
      return ManagedValue::forUnmanaged(value);

   auto &cleanup = Cleanups.pushCleanup<FormalAccessReleaseValueCleanup>();
   CleanupHandle handle = Cleanups.getTopCleanup();
   FormalEvalContext.push<OwnedFormalAccess>(loc, handle, value);
   cleanup.Depth = FormalEvalContext.stable_begin();
   return ManagedValue(value, handle);
}

CleanupHandle PILGenFunction::enterDormantFormalAccessTemporaryCleanup(
   PILValue addr, PILLocation loc, const TypeLowering &tempTL) {
   assert(isInFormalEvaluationScope() && "Must be in formal evaluation scope");
   if (tempTL.isTrivial())
      return CleanupHandle::invalid();

   auto &cleanup = Cleanups.pushCleanup<FormalAccessReleaseValueCleanup>();
   CleanupHandle handle = Cleanups.getTopCleanup();
   Cleanups.setCleanupState(handle, CleanupState::Dormant);
   FormalEvalContext.push<OwnedFormalAccess>(loc, handle, addr);
   cleanup.Depth = FormalEvalContext.stable_begin();
   return handle;
}

void PILGenFunction::destroyLocalVariable(PILLocation silLoc, VarDecl *vd) {
   assert(vd->getDeclContext()->isLocalContext() &&
          "can't emit a local var for a non-local var decl");
   assert(vd->hasStorage() && "can't emit storage for a computed variable");

   assert(VarLocs.count(vd) && "var decl wasn't emitted?!");

   auto loc = VarLocs[vd];

   // For a heap variable, the box is responsible for the value. We just need
   // to give up our retain count on it.
   if (loc.box) {
      B.emitDestroyValueOperation(silLoc, loc.box);
      return;
   }

   // For 'let' bindings, we emit a release_value or destroy_addr, depending on
   // whether we have an address or not.
   PILValue Val = loc.value;
   if (!Val->getType().isAddress())
      B.emitDestroyValueOperation(silLoc, Val);
   else
      B.createDestroyAddr(silLoc, Val);
}

void PILGenFunction::deallocateUninitializedLocalVariable(PILLocation silLoc,
                                                          VarDecl *vd) {
   assert(vd->getDeclContext()->isLocalContext() &&
          "can't emit a local var for a non-local var decl");
   assert(vd->hasStorage() && "can't emit storage for a computed variable");

   assert(VarLocs.count(vd) && "var decl wasn't emitted?!");

   auto loc = VarLocs[vd];

   // Ignore let values captured without a memory location.
   if (!loc.value->getType().isAddress()) return;

   assert(loc.box && "captured var should have been given a box");
   B.createDeallocBox(silLoc, loc.box);
}
